package com.changzhi.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.changzhi.common.constans.Constants;
import com.changzhi.common.constans.RedisConstans;
import com.changzhi.common.constants.RedisConstants;
import com.changzhi.common.enums.ActivityConfigEnum;
import com.changzhi.common.enums.SysDictCodeEnum;
import com.changzhi.common.exception.ServiceException;
import com.changzhi.common.mq.MqProducer;
import com.changzhi.common.util.DateUtil;
import com.changzhi.ldyl.model.*;
import com.changzhi.ldyl.model.dto.LoadDto;
import com.changzhi.ldyl.model.dto.PrizeDto;
import com.changzhi.ldyl.model.vo.ChannelVo;
import com.changzhi.ldyl.model.vo.RedVo;
import com.changzhi.ldyl.model.vo.UserVo;
import com.changzhi.mapper.*;
import com.changzhi.service.*;
import com.changzhi.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @Description:
 * @author: 小落
 * @date: 2021年12月01日
 */
@Slf4j
@Service
public class RedServiceImpl implements RedService {

	@Autowired
	private ChatChannelService chatChannelService;
	@Autowired
	private ChatChannelShareRatioConfigMapper chatChannelShareRatioConfigMapper;
	@Resource
	private ActivityGenerateMapper activityGenerateMapper;
	@Autowired
	private ActivityService activityService;
	@Autowired
	private MqProducer mqProducer;
	@Resource
	private ActivityConfigMapper activityConfigMapper;
	@Autowired
	private RedisUtil redisUtil;
	@Autowired
	private GiftService giftService;
	@Resource
	private ActivityDevoteMapper activityDevoteMapper;
	@Autowired
	private ActivityWalletMapper activityWalletMapper;
	@Resource
	private ActivityMapper activityMapper;
	@Autowired
	private SysDictMapper sysDictMapper;

	@Autowired
	private Executor executor;
	private ReentrantLock lock = new ReentrantLock();
	@Resource
	private ActivityAwardMapper activityAwardMapper;
	public static Map<String, LocalDateTime> TIME_MAP = new HashMap<>();
	public final static Integer ACTIVITY_ID = 42;
	public static Integer TARGET_VALUE = 500000;
	public final static String RED = "0";
	public final static String GOLD = "1";
	@Value("${spring.profiles.active}")
	private String env;

	@PostConstruct
	public void init(){
		redisUtil.del(RedisConstans.ACTIVITY + ACTIVITY_ID);
		Activity activity = activityService.getActivity(ACTIVITY_ID);
		//存活动时间
		TIME_MAP.put("startTime", activity.getStartTime());
		TIME_MAP.put("endTime", activity.getEndTime());
		TARGET_VALUE = Integer.valueOf(activity.getProperty1());
	}

	@Override
	public void doMqDataByUserGift(UserGift userGift) {
		//1.是否在活动时间内
		if(!DateUtil.isBetweenTime(TIME_MAP.get("startTime"), TIME_MAP.get("endTime"))){
			//测试账号通行
			if(!activityService.isTestAccount(userGift.getUserId())){
				return;
			}
		}
		String userId = userGift.getUserId();
		Integer value = userGift.getTotalGiftValue();
		String channelId = userGift.getChannelId();
		if(value <= 0){
			return;
		}
		//回合id获取
		String idKey = RedisConstans.ACTIVITY_RED_CURRENT_ROUND_ID + ACTIVITY_ID + channelId;
		Integer roundId = redisUtil.get(idKey);
		if (roundId == null) {
			//没有场次i，则生成
			roundId = generateId("红色" + DateUtil.localDateTimeToString(LocalDateTime.now()) + "场次", Integer.valueOf(RED), channelId);
			redisUtil.set(idKey, roundId,RedisConstans.ONE_MONTH);
		}
		//当前活动的剩余钻石
		String diamondKey = RedisConstans.putRediskey(RedisConstans.ACTIVITY_RED_CURRENT_DIAMOND, ACTIVITY_ID, channelId);
		long currentDiamond = redisUtil.incr(diamondKey, value, RedisConstans.ONE_MONTH);
		updateValue(channelId, value, 0);
		//判断是否小于目标值
		if(currentDiamond < TARGET_VALUE){
			//推送当前值到mq
			RedVo redVo = new RedVo();
			redVo.setChannelId(channelId);
			redVo.setDialogType(RED);
			redVo.setIconType(RED);
			redVo.setCurrentValue(currentDiamond);
			redVo.setTotalValue(Long.valueOf(TARGET_VALUE));
			mqProducer.sendRedInfo(JSON.toJSONString(redVo));
			//当前回合用户贡献值
			doUserDevote(channelId, userId, roundId, value);
			return;
		}
		int count = 1;
		Integer reduceValue = 0;
		List<Integer> roundIdList = new ArrayList<>();
		long decr = 0;
		while (true){
			//减去一次出现红包雨需要的钻石值
			decr = redisUtil.decr(diamondKey, TARGET_VALUE);
			//计算生成红包的次数
			roundIdList.add(roundId);
			//当前回合用户贡献值
			if(count == 1){
				int oldValue = (int) (currentDiamond - value);
				reduceValue = TARGET_VALUE - oldValue;
				doUserDevote(channelId, userId, roundId, reduceValue);
			} else {
				doUserDevote(channelId, userId, roundId, TARGET_VALUE);
				reduceValue = TARGET_VALUE;
			}
			//删除上一回合的id
			redisUtil.del(idKey);
			//生成下一回合id
			roundId = generateId("红色" + DateUtil.localDateTimeToString(LocalDateTime.now()) + "场次", Integer.valueOf(RED), channelId);
			redisUtil.set(idKey, roundId,RedisConstans.ONE_MONTH);
			//若小于目标值，则退出循环
			value = value - reduceValue;
			if(value  < TARGET_VALUE){
				if (value > 0) {
					doUserDevote(channelId, userId, roundId, value);
				}
				break;
			}
			count++;
			if(count > 9999){
				log.error("method：死循环，强制退出, userGift={}", JSON.toJSONString(userGift));
				break;
			}
		}
		updateValue(channelId, count, 1);
		for (Integer round : roundIdList) {
			//生成红包场次数据
			generateRoundData(round, RED, channelId);
			log.info("methodName:doMqDataByUserGift，红包生成，channelId={}，round={}", channelId, round);
		}
		//推送当前值到mq
		if(decr != 0){
			RedVo redVo = new RedVo();
			redVo.setChannelId(channelId);
			redVo.setDialogType(RED);
			redVo.setIconType(RED);
			redVo.setCurrentValue(decr);
			redVo.setTotalValue(Long.valueOf(TARGET_VALUE));
			mqProducer.sendRedInfo(JSON.toJSONString(redVo));
		}
	}


	//19、22点在10人以上的公开厅出现
	@Override
	public void jobTask() {

		//判断活动是否开启
		Activity activity = getActivity(ACTIVITY_ID);
		//1.是否在活动时间内
		if(null ==  activity || !DateUtil.isBetweenTime(activity.getStartTime(), activity.getEndTime())){
			return;
		}

		//条件1：查询出所有公开厅
		LambdaQueryWrapper<ChatChannelShareRatioConfig> shareWrapper = new LambdaQueryWrapper<>();
		shareWrapper.select(ChatChannelShareRatioConfig::getChannelId).eq(ChatChannelShareRatioConfig::getStatus, 1);
		List<ChatChannelShareRatioConfig> list = chatChannelShareRatioConfigMapper.selectList(shareWrapper);
		List<String> channelIdList = new ArrayList<>();
		for (ChatChannelShareRatioConfig ratioConfig : list) {
			//条件2:该厅在10人以上
			ChatChannel chatChannel = chatChannelService.getById(ratioConfig.getChannelId());
			if (chatChannel != null && chatChannel.getOnlineNumber() != null && chatChannel.getOnlineNumber() > 10) {
				channelIdList.add(chatChannel.getChannelId());
			}
		}
		log.warn("methodName:jobTask,list.size={},size={},channelIdList={}", list.size(), channelIdList.size(),JSON.toJSONString(channelIdList));


		// TODO: 2022/1/6 测试房间
		/*channelIdList = new ArrayList<>();
		if("gray".equals(env)){
			channelIdList.add("1306872383956582401");
			channelIdList.add("1308228267492245505");
		} else {
			channelIdList.add("1338348217682624513");
			channelIdList.add("1295253461415231489");
		}*/


		//生成id
		Integer roundId = generateId("金色" + DateUtil.localDateTimeToString(LocalDateTime.now()) + "场次", Integer.valueOf(GOLD), "");
		//生成红包场次数据
		for (String channelId : channelIdList) {
			//生成红包场次数据
			generateRoundData(roundId, GOLD, channelId);
		}
	}


	//获取活动数据
	private Activity getActivity(Integer id) {
		Activity activity = null;
		activity = activityMapper.selectById(id);
		return activity;
	}


	@Override
	public void doMqDataByActivityAward(String msg) {
		Map<String, Object> map = JSON.parseObject(msg, Map.class);
		Integer activityId = (Integer) map.get("activityId");
		Integer roundId = (Integer) map.get("roundId");
		String userId = (String) map.get("userId");
		if(StringUtils.isBlank(userId)){
			return;
		}
		JSONArray jsonArray = (JSONArray) map.get("awardList");
		List<ActivityPrize> awardList = JSONArray.parseArray(jsonArray.toJSONString(), ActivityPrize.class);
		if(CollectionUtils.isEmpty(awardList)){
			return;
		}
		if(!ACTIVITY_ID.equals(activityId)){
			return;
		}
		String key = RedisConstans.putRediskey(RedisConstans.PRIZE_RECORD, activityId, roundId, userId);
		if(redisUtil.get(key) != null){
			log.error("methodName:doMqDataByActivityAward,error={}, msg={}", "请勿重复领取奖励", msg);
			return;
		}
		for (ActivityPrize prize : awardList) {
			prize.setRoundId(roundId);
			if(prize.getSource() == 2){
				giftService.saveUserDress(new PrizeDto(userId, prize, "红包雨获得"));
			} else if(prize.getSource() == 1){
				giftService.savePrize(new PrizeDto(userId, prize, "红包雨获得"));
			} else if(prize.getSource() == 0){
				giftService.savePrize(new PrizeDto(userId, prize));
			} else {
				log.error("methodName:doMqDataByActivityAward, source有误, msg={}", msg);
			}
		}
		redisUtil.set(key, 1, RedisConstans.TWO_MONTH);

	}

	@Override
	public List<UserVo> lastAwardList(Integer limit) {
		List<UserVo> list = activityAwardMapper.selectAwardList(ACTIVITY_ID, 200);
		for (UserVo userVo : list) {
			if(StringUtils.isBlank(userVo.getAvatarUrl())){
				userVo.setAvatarUrl(Constants.DEFAULT_AVATAR);
			}
		}
		return list;
	}

	@Override
	public List<UserVo> myRecordList(LoadDto loadDto) {
		if(loadDto.getType() == null){
			throw new ServiceException("类型不能为空");
		}
		//查roundId
		loadDto.setActivityId(ACTIVITY_ID);
		List<Integer> roundIdList = activityAwardMapper.selectMyRedRoundIdList(loadDto);
		if(CollectionUtils.isEmpty(roundIdList)){
			return new ArrayList<>(1);
		}
		loadDto.setIdList(roundIdList);
		List<ActivityAward> awardList = activityAwardMapper.selectMyRedRecord(loadDto);
		//分组
		Map<Integer, List<ActivityAward>> listMap = awardList.stream().collect(Collectors.groupingBy(ActivityAward::getRoundId));
		//返回封装
		List<UserVo> list = new ArrayList<>();
		for (Map.Entry<Integer, List<ActivityAward>> entry : listMap.entrySet()) {
			Integer roundId= entry.getKey();
			List<ActivityAward> awards = entry.getValue();

			List<UserVo> prizeList = new ArrayList<>();
			for (ActivityAward award : awards) {
				UserVo userVo = new UserVo();
				userVo.setGiftIcon(award.getIcon()).setGiftName(award.getName()).setNum(award.getNum());
				prizeList.add(userVo);
			}
			UserVo vo = new UserVo();

			vo.setCreateTimeStr(DateUtil.localDateTimeToString(awards.get(0).getCreateTime())).setId(roundId).setPrizeList(prizeList);
			vo.setTimestamp( awards.get(0).getCreateTime().toInstant(ZoneOffset.of("+8")).toEpochMilli());
			list.add(vo);
		}
		list.sort(Comparator.comparing(UserVo::getId, Comparator.reverseOrder()));
		return list;
	}

	@Override
	public ChannelVo luckChannel() {
		ChannelVo vo = new ChannelVo();
		LambdaQueryWrapper<ActivityWallet> wrapper = new LambdaQueryWrapper<>();
		wrapper.select(ActivityWallet::getUserId).eq(ActivityWallet::getActivityId, ACTIVITY_ID).gt(ActivityWallet::getAmount2, 0).orderByDesc(ActivityWallet::getAmount2).last("limit 1");
		ActivityWallet activityWallet = activityWalletMapper.selectOne(wrapper);
		if (activityWallet == null) {
			return vo;
		}
		ChatChannel chatChannel = chatChannelService.getChatChannel(activityWallet.getUserId());
		if (chatChannel == null) {
			return vo;
		}
		vo.setOwnerAccountId(chatChannel.getOwnerAccountid());
		vo.setChannelId(chatChannel.getChannelId());
		vo.setChannelAvatarUrl(chatChannel.getAvatarUrl());
		vo.setChannelName(chatChannel.getChannelName());
		return vo;
	}

	//生成红包场次数据
	private void generateRoundData(Integer roundId, String type, String channelId){
		//生成红包场次数据
		RedVo redVo = new RedVo();
		redVo.setRoundId(roundId);
		redVo.setDialogType(type);
		if(RED.equals(type)){
			redVo.setIconType(type);
		}
		redVo.setChannelId(channelId);
		//判断当前红包场次数据是否有
		String key = RedisConstans.putRediskey(RedisConstans.ACTIVITY_RED_CURRENT_INFO, ACTIVITY_ID, redVo.getChannelId());
		RedVo cachedRedVo = redisUtil.get(key);
		if (cachedRedVo == null) {
			pushRedData(redVo);
			return;
		}
		//前红包场次数据没有的情况
		//1.放入队列
		String queueKey = RedisConstans.putRediskey(RedisConstans.ACTIVITY_RED_CURRENT_QUEUE, ACTIVITY_ID, channelId);
		redVo.setCurrentTimeStamp(System.currentTimeMillis() / 1000);
		redisUtil.lSet(queueKey, redVo,RedisConstans.ONE_MONTH);
		//2.将房间id放到一个hash缓存中，用间隔定时任务去扫描
		String channelKey = RedisConstans.putRediskey(RedisConstans.ACTIVITY_RED_CURRENT_CHANNEL_ID, ACTIVITY_ID);
		Map<String, Object> map = (Map<String, Object>) redisUtil.hget(channelKey, channelId);
		if(map == null){
			map = new HashMap<>();
			map.put("channelId", channelId);
			map.put("second", 300);
			redisUtil.hset(channelKey, channelId, map, 300);
		} else {
			Integer second = (Integer) map.get("second");
			map.put("second", second + 300);
			redisUtil.hset(channelKey, channelId, map, second + 300);
		}
	}

	//每隔10秒扫描一次
	@Scheduled(fixedRate = 10000)
	public void asyncTask() {
		String channelKey = RedisConstans.putRediskey(RedisConstans.ACTIVITY_RED_CURRENT_CHANNEL_ID, ACTIVITY_ID);
		List<Map<String, Object>> mapList = redisUtil.hGetValuesList(channelKey);
		if(CollectionUtils.isEmpty(mapList)){
			return;
		}
		log.info("methodName:asyncTask,size={},data={}", mapList.size(), JSON.toJSONString(mapList));
		for (Map<String, Object> map : mapList) {
			String channelId = (String) map.get("channelId");
			String key = RedisConstans.putRediskey(RedisConstans.ACTIVITY_RED_CURRENT_INFO, ACTIVITY_ID, channelId);
			RedVo cachedRedVo = redisUtil.get(key);
			if (cachedRedVo == null) {
				String queueKey = RedisConstans.putRediskey(RedisConstans.ACTIVITY_RED_CURRENT_QUEUE, ACTIVITY_ID, channelId);
				RedVo redVo = redisUtil.lLeftPop(queueKey);
				if (redVo != null) {
					pushRedData(redVo);
				}
			}
		}
	}


	//推送数据并放到缓存中
	private void pushRedData(RedVo redVo){
		try {
			lock.lock();
			//再判断一次缓存中是否有值
			String key = RedisConstans.putRediskey(RedisConstans.ACTIVITY_RED_CURRENT_INFO, ACTIVITY_ID, redVo.getChannelId());
			RedVo cachedRedVo = redisUtil.get(key);
			if (cachedRedVo != null) {
				//重新放入队列
				log.error("methodName:pushRedData,并发冲突，redVo={}", JSON.toJSONString(redVo));
				String queueKey = RedisConstans.putRediskey(RedisConstans.ACTIVITY_RED_CURRENT_QUEUE, ACTIVITY_ID, redVo.getChannelId());
				redisUtil.lSet(queueKey, redVo,RedisConstans.ONE_MONTH);
				return;
			}
			//放到当前要显示的队列中
			LocalDateTime now = LocalDateTime.now();
			redVo.setCurrentTimeStamp(now.toEpochSecond(ZoneOffset.of("+8")));
			redVo.setExpireTimeStamp(now.plusSeconds(60).toEpochSecond(ZoneOffset.of("+8")));
			redisUtil.set(key, redVo, 95);
			if(RED.equals(redVo.getDialogType())){
				//当前活动的剩余钻石
				String diamondKey = RedisConstans.putRediskey(RedisConstans.ACTIVITY_RED_CURRENT_DIAMOND, ACTIVITY_ID, redVo.getChannelId());
				Integer surplusDiamond = redisUtil.get(diamondKey);
				surplusDiamond = surplusDiamond == null ? 0 : surplusDiamond;
				redVo.setCurrentValue(surplusDiamond.longValue());
				redVo.setTotalValue(Long.valueOf(TARGET_VALUE));
			}
			//推送消息
			mqProducer.sendRedInfo(JSON.toJSONString(redVo));
			//推送横幅
			if("0".equals(redVo.getDialogType())){
				//
				if(activityService.checkNormalMode(redVo.getChannelId())){
					//查询配置
					LambdaQueryWrapper<ActivityConfig> configWrapper = new LambdaQueryWrapper<>();
					configWrapper.eq(ActivityConfig::getActivityId, ACTIVITY_ID).eq(ActivityConfig::getType, redVo.getDialogType());
					List<ActivityConfig> configList = activityConfigMapper.selectList(configWrapper);
					if(CollectionUtils.isEmpty(configList)){
						log.error("没有配置信息");
						return;
					}
					List<ActivityConfig> configList2 = configList.stream().filter(t -> t.getSubType().equals(ActivityConfigEnum.CHRISTMAS_BANNER.getType())).collect(Collectors.toList());
					if(CollectionUtils.isEmpty(configList2)){
						log.error("横幅盘配置不存在");
						return;
					}
					ActivityConfig config = configList2.get(0);
					ChatChannel channel = chatChannelService.getChatChannel(redVo.getChannelId());
					if (channel != null) {
						log.error("methodName：推送了横幅,id={}" + redVo.getRoundId());
						// TODO: 2022/1/6 测试不发横幅
						SysDict sysDict = sysDictMapper.selectOne(new QueryWrapper<SysDict>().eq(SysDict.CODE, SysDictCodeEnum.PUSH_RED_DATA).eq(SysDict.STATUS, "VALID").last("limit 1"));
						if (null != sysDict && StrUtil.equals(sysDict.getProperty1(),Constants.OPEN)) {
							activityService.pushBanner(1, "红色掉落" + redVo.getRoundId(), String.format(config.getContent(), channel.getOwnerAccountid(), channel.getChannelName()), config.getPicture(), redVo.getChannelId(), 12000L);
						}
					}
				} else{
					log.error("methodName：pushRedData,非正常模式房间，不推送横幅，channelId={}", redVo.getChannelId());
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	//生产场次id
	private Integer generateId(String name, Integer type, String remark){
		ActivityGenerate generate = new ActivityGenerate();
		generate.setActivityId(ACTIVITY_ID);
		generate.setName(name);
		generate.setType(type);
		generate.setRemark(remark);
		activityGenerateMapper.insert(generate);
		return generate.getId();
	}

	private void updateValue(String channelId, Integer value, Integer type){
		//更新总值和当前剩余值到数据库
		String channelKey = RedisConstans.putRediskey(RedisConstans.ACTIVITY_FLAG, ACTIVITY_ID, channelId);
		Integer id = redisUtil.get(channelKey);
		if (id == null) {
			LambdaQueryWrapper<ActivityWallet> wrapper = new LambdaQueryWrapper<>();
			wrapper.eq(ActivityWallet::getActivityId, ACTIVITY_ID).eq(ActivityWallet::getUserId, channelId).last("limit 1");
			ActivityWallet activityWallet = activityWalletMapper.selectOne(wrapper);
			if(activityWallet != null){
				if(type == 0){
					activityWalletMapper.addAmount1(activityWallet.getId(), value);
				} else {
					activityWalletMapper.addAmount2(activityWallet.getId(), value);
				}
			} else {
				ActivityWallet record = new ActivityWallet();
				record.setActivityId(ACTIVITY_ID);
				record.setUserId(channelId);
				record.setAmount1(value);
				activityWalletMapper.insert(record);
				redisUtil.set(channelKey, record.getId(), RedisConstans.TWO_MONTH);
			}
		} else{
			if(type == 0){
				activityWalletMapper.addAmount1(id, value);
			} else {
				activityWalletMapper.addAmount2(id, value);
			}
		}
	}

	//当前回合用户贡献值
	private void doUserDevote(String channelId, String userId, Integer roundId, Integer value){
		//每个用户总值
		String key = RedisConstans.putRediskey(RedisConstans.ACTIVITY_FLAG, ACTIVITY_ID, channelId, userId, roundId);
		Integer id = redisUtil.get(key);
		if (id == null) {
			LambdaQueryWrapper<ActivityDevote> wrapper = new LambdaQueryWrapper<>();
			wrapper.eq(ActivityDevote::getActivityId, ACTIVITY_ID).eq(ActivityDevote::getChannelId, channelId).eq(ActivityDevote::getUserId, userId).eq(ActivityDevote::getRoundId, roundId);
			ActivityDevote record = activityDevoteMapper.selectOne(wrapper);
			if (record != null) {
				activityDevoteMapper.addNum(record.getId(), value);
			} else {
				record = new ActivityDevote();
				record.setActivityId(ACTIVITY_ID);
				record.setChannelId(channelId);
				record.setUserId(userId);
				record.setRoundId(roundId);
				record.setNum(value);
				record.setTotal(TARGET_VALUE.intValue());
				activityDevoteMapper.insert(record);
				redisUtil.set(key, record.getId(), RedisConstans.TWO_WEEK);
			}
		} else{
			activityDevoteMapper.addNum(id, value);
		}
	}
}
